#pragma once
#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Value.hpp>
#include <etcd/Watcher.hpp>
#include <functional>
#include <iostream>
#include <memory>
#include "../logger.hpp"

namespace shared {

namespace infra {

/* ================================
 *        服务注册客户端类
 * ================================ */
class Registrant
{
public:
    Registrant(const std::string& host)
       : client_(make_shared<etcd::Client>(host))
       , keepAlive_(client_->leasekeepalive(3).get())
       , leaseId_(keepAlive_->Lease())
    {}
    ~Registrant() { keepAlive_->Cancel(); }
    
    bool registry(const std::string& key, const std::string& val)
    {
        etcd::Response resp = client_->put(key, val, leaseId_).get();
        if(!resp.is_ok()) {
            LOG_ERROR("注册数据失败: {}", resp.error_message());
            return false;
        }
        return true;
    }
private:
    std::shared_ptr<etcd::Client> client_;
    std::shared_ptr<etcd::KeepAlive> keepAlive_; 
    uint64_t leaseId_;
};

/* ================================
 *        服务发现客户端类
 * ================================ */
class Discovery
{
    using NotifyCallback = std::function<void(std::string, std::string)>;
public:
    Discovery(const std::string& host, const std::string& basedir, const NotifyCallback& putCb, const NotifyCallback& delCb)
        : client_(make_shared<etcd::Client>(host))
        , putCb_(putCb), delCb_(delCb)
    { 
        etcd::Response resp = client_->ls(basedir).get();
        if(!resp.is_ok()) {
            LOG_ERROR("获取数据失败: {}", resp.error_message());
            return;
        } 
        int n = resp.keys().size();
        for (int i = 0; i < n; ++i) {
            LOG_DEBUG("{} 可以提供 {} 服务", resp.value(i).as_string(), resp.key(i));
            if(putCb_) {
                putCb_(resp.key(i), resp.value(i).as_string());
            }
        }
        watcher_ = make_shared<etcd::Watcher>(
            *client_.get(), basedir, std::bind(&Discovery::callback, this, std::placeholders::_1), true
        );
    }
private:
    void callback(const etcd::Response& resp)
    {
        if(!resp.is_ok()) {
            LOG_ERROR("错误的事件通知: {}", resp.error_message());
        } 
        for(const auto& ev : resp.events()) {
            if(ev.event_type() == etcd::Event::EventType::PUT) {
                if(putCb_) putCb_(ev.kv().key(), ev.kv().as_string());
                LOG_DEBUG("新增服务: {}--{}", ev.kv().key(), ev.kv().as_string());
            } else if(ev.event_type() == etcd::Event::EventType::DELETE_) {
                if(delCb_) delCb_(ev.prev_kv().key(), ev.prev_kv().as_string());
                LOG_DEBUG("删除服务: {}--{}", ev.prev_kv().key(), ev.prev_kv().as_string());
            } else {
                LOG_ERROR("错误的事件通知类型");
            }
        }
    }

private:
    std::shared_ptr<etcd::Watcher> watcher_;
    std::shared_ptr<etcd::Client> client_;
    NotifyCallback putCb_;
    NotifyCallback delCb_;
};

using RegistrantPtr = shared_ptr<Registrant>;
using DiscoveryPtr = shared_ptr<Discovery>;

}}